package project.newCode.surfaces;

import java.awt.Color;
import java.util.ArrayList;
import java.util.Vector;

import project.hulpPakket.Color3f;
import project.hulpPakket.Point3f;
import project.hulpPakket.TexCoord2f;
import project.hulpPakket.Vector3f;
import project.newCode.Hitrecord;
import project.newCode.Ray;
import project.newCode.materials.Material;

public class IndexedTriangleSet extends Surface{
	
	private Point3f[] coordinates;
	private Vector3f[] normals;
	private TexCoord2f[] textureCoordinates;
	private int[] ci;
	private int[] ni;
	private int[] ti;
	private int numfaces;
	private Box box;

	public IndexedTriangleSet(Point3f[] cv, Vector3f[] nv,
			TexCoord2f[] tv, int[] ci,
			int[] ni, int[] ti, int numfaces, Box boundingbox, Material m) {
		super(m);
		this.coordinates = cv;
		this.normals = nv;
		this.textureCoordinates = tv;
		this.ci = ci;
		this.ni = ni;
		this.ti = ti;
		this.numfaces = numfaces;
		this.box = boundingbox;
	}

	@Override
	public boolean hit(Ray hittingray, double t0, double t1, Hitrecord rec) {
		boolean hit = false;
		for(int i = 0; i<numfaces; i++) {
			Point3f[] p = {coordinates[ci[3*i]-1],coordinates[ci[3*i+1]-1],coordinates[ci[3*i+2]-1]};
			if(hitTriangle(hittingray, t0, t1, rec, p)) {
				t1 = rec.t;
				hit = true;
				Vector3f bary = baryCentrics(rec.intersection,p);
				rec.normal = Vector3f.Plus(normals[ni[3*i]-1].multiply(bary.x), Vector3f.Plus(normals[ni[3*i+1]-1].multiply(bary.y),normals[ni[3*i+2]-1].multiply(bary.z) )).normalise();
				if(textureCoordinates.length>0) {
					rec.tex = TexCoord2f.bary(textureCoordinates[ti[3*i]-1], bary.x, textureCoordinates[ti[3*i+1]-1], bary.y, textureCoordinates[ti[3*i+2]-1], bary.z);
				}
				this.setMaterialProperties(rec);
			}
		}
		rec.hitCounter = numfaces;
		return hit;
	}
	public boolean hitTriangle(Ray r, double t0, double t1, Hitrecord rec, Point3f[] p) {
		float a = p[0].x-p[1].x;
		float b = p[0].y-p[1].y;
		float c = p[0].z-p[1].z;
		float d = p[0].x-p[2].x;
		float e = p[0].y-p[2].y;
		float f = p[0].z-p[2].z;
		float g = r.getDirection().x;
		float h = r.getDirection().y;
		float i = r.getDirection().z;
		float j = p[0].x-r.getOrigin().x;
		float k = p[0].y-r.getOrigin().y;
		float l = p[0].z-r.getOrigin().z;
		float eiminhf = e*i - h*f;
		float gfmindi = g*f - d*i;
		float dhmineg = d*h - e*g;
		float M = a*eiminhf+b*gfmindi+c*dhmineg;
		if(M==0) {
			System.out.println("Determinant zero = no unique solution!!");
			return false;
		}
		float t = -1*(f*(a*k-j*b)+e*(j*c-a*l)+d*(b*l-k*c))/M;
		if(t < t0 || t > t1) {
			return false;
		}
		float gamma = (i*(a*k-j*b)+h*(j*c-a*l)+g*(b*l-k*c))/M;
		if(gamma < 0 || gamma > 1) {
			return false;
		}
		float beta = (j*eiminhf+k*gfmindi+l*dhmineg)/M;
		if(beta < 0 || beta > (1-gamma)) {
			return false;
		}
		rec.t = t;
		rec.intersection = Point3f.Plus(r.getOrigin(), r.getDirection().multiply(t));
		rec.normal = Vector3f.cross(Point3f.Min(p[1], p[0]), Point3f.Min(p[2],p[0])).normalise();
		// Formule boek pagina 48
		return true;
	}
	public Vector3f baryCentrics(Point3f p, Point3f[] pset) {
		Vector3f n = Vector3f.cross(Point3f.Min(pset[1], pset[0]), Point3f.Min(pset[2],pset[0]));
		Vector3f na = Vector3f.cross(Point3f.Min(pset[2], pset[1]), Point3f.Min(p,pset[1]));
		Vector3f nb = Vector3f.cross(Point3f.Min(pset[0], pset[2]), Point3f.Min(p,pset[2]));
		Vector3f nc = Vector3f.cross(Point3f.Min(pset[1], pset[0]), Point3f.Min(p,pset[0]));
		float normn2=(float) n.norm();
		return new Vector3f(Vector3f.Inp(n, na)/normn2,Vector3f.Inp(n, nb)/normn2,Vector3f.Inp(n, nc)/normn2);
	}
	@Override
	public ArrayList<Surface> getSurfaceList(){
		ArrayList<Surface> s = new ArrayList<Surface>();
		for(int i = 0; i<numfaces; i++) {
			Point3f[] p = {coordinates[ci[3*i]-1].clone(),coordinates[ci[3*i+1]-1].clone(),coordinates[ci[3*i+2]-1].clone()};
			Vector3f [] n = {normals[ni[3*i]-1].clone(),normals[ni[3*i+1]-1].clone(),normals[ni[3*i+2]-1].clone()};
			TexCoord2f [] t = {new TexCoord2f(0,0),new TexCoord2f(0,1),new TexCoord2f(1,1)};
			if(textureCoordinates.length>0) {
				t[0]=textureCoordinates[ti[3*i]-1];
				t[1]=textureCoordinates[ti[3*i+1]-1];
				t[2]=textureCoordinates[ti[3*i+2]-1];
			}
			s.add(new Triangle(p,t,n,this.getMaterial()));
 		}
		return s;
	}

	@Override
	public Box boundingbox() {
		return box;
	}
	// This class represents a triangle mesh;

	@Override
	public Surface surfaceClone() {
		return new IndexedTriangleSet(this.coordinates.clone(),this.normals.clone(),this.textureCoordinates.clone(),this.ci.clone(),this.ni.clone(),this.ti.clone(), numfaces,this.box.surfaceClone(), this.getMaterial());
	}
}
